package ez

import (
	"gitee.com/dreamwood/ez-go/tools"
	"strings"
)

var routeHub *RouteHub

type RouteHub struct {
	//直接匹配的路由
	DirectRouter map[string]*EzHandler
	//正则匹配的路由
	RegxRouter map[string]*EzHandler
	//全局前置中间件
	Preparer map[string]*EzAction
	//全局后置中间件
	Finisher map[string]*EzAction
	//Handler前置操作,区别于前置操作，这时已经可以获取到route等信息了
	BeforeHandler map[string]*EzAction
	//Handler后置操作
	AfterHandler map[string]*EzAction
}

func NewRouteHub() *RouteHub {
	return &RouteHub{
		DirectRouter:  make(map[string]*EzHandler),
		RegxRouter:    make(map[string]*EzHandler),
		Preparer:      make(map[string]*EzAction),
		Finisher:      make(map[string]*EzAction),
		BeforeHandler: make(map[string]*EzAction),
		AfterHandler:  make(map[string]*EzAction),
	}
}

func (this *RouteHub) AddDirectRoute(router *Router, action *EzAction) *EzHandler {
	hd := NewEzHandler(router, action)
	url := ConfigService.RouterPrefix + router.Group + router.Prefix + router.Url + router.Suffix
	this.DirectRouter[url] = hd
	return hd
}

func (this *RouteHub) AddRegxRoute(router *Router, action *EzAction) *EzHandler {
	//处理字符串转成合适的正则表达式 例如:id(^\d+$)

	url := ConfigService.RouterPrefix + router.Group + router.Prefix + router.Url + router.Suffix
	pattern := tools.ReplaceAll(url, `:(\w+)\((.+?)\)`, "(?P<$1>$2)")
	hd := NewEzHandler(router, action)
	hd.Matcher = pattern
	this.RegxRouter[url] = hd
	return hd
}

type Router struct {
	Url    string
	Name   string
	Group  string
	Prefix string
	Suffix string
}

func CreateApi(router *Router, action EzAction) *EzHandler {
	url := ConfigService.RouterPrefix + router.Group + router.Prefix + router.Url + router.Suffix
	if strings.Contains(url, ":") {
		return routeHub.AddRegxRoute(router, &action)
	} else {
		//直接路由注册
		return routeHub.AddDirectRoute(router, &action)
	}
}

func Dispatch(session *Session) {
	//有控制中心进行预处理的场景可以直接找到对应的处理程序
	//todo 控制中心进行处理完成之后把结果写在header头
	//todo 通过权限控制定位到matcher,同时确定是指向路由还是正则路由

	//没有注册中心的处理方式
	tRouter := session.Timer.Add("Router")
	for url, hd := range routeHub.DirectRouter {
		if url == session.Input.Url {
			tRouter.Done()
			PublicHandle(hd, session)
			return
		}
	}
	for _, hd := range routeHub.RegxRouter {
		if tools.Match(session.Input.Url, hd.Matcher) {
			names := tools.MatchAll(hd.Matcher, `<(.+?)>`)
			matches := tools.MatchAll(session.Input.Url, hd.Matcher)
			rebuild := make(map[string]*InputAnyThing)
			for index, name := range names {
				rebuild[name[1]] = NewInputAnyThing(matches[index][1])
			}
			session.Input.UrlData = rebuild
			tRouter.Done()
			PublicHandle(hd, session)
			return
		}
	}
	tRouter.Done()
	urlNotFound(session)
}

func PublicHandle(handler *EzHandler, session *Session) {
	defer func() {
		//执行过程中的所有数据都写入了Writer
		_, e := session.Output.Response.Write(session.Output.Writer.Bytes())
		if e != nil {
			println("Can not write to response", e.Error())
		}
	}()

	//加入日志系统
	//LogToConsole(fmt.Sprintf("【%s】Url:%s, Match:%s", session.Input.Request.Method, session.Input.Url, handler.Matcher))
	//在此之前handler是空的
	session.Handler = handler
	tBefore := session.Timer.Add("BeforeHandler")
	//添加一些公共的前置操作
	for name, action := range routeHub.BeforeHandler {
		tPrepareItem := session.Timer.Add(name)
		(*action)(session)
		tPrepareItem.Done()
		if session.IsStop {
			return
		}
	}
	tBefore.Done()

	tMainHandler := session.Timer.Add("MainHandler")
	//执行处理
	handler.Run(session)
	tMainHandler.Done()

	tAfter := session.Timer.Add("GlobalFinisher")
	//添加一些公共的后续操作

	for name, action := range routeHub.AfterHandler {
		tFinisherItem := session.Timer.Add(name)
		(*action)(session)
		tFinisherItem.Done()
		if session.IsStop {
			return
		}
	}
	tAfter.Done()

}

func urlNotFound(session *Session) {
	session.Output.Response.WriteHeader(404)
	session.Output.Response.Write([]byte("URL Not Match"))
}

func init() {
	routeHub = NewRouteHub()
}

func AddPreparer(name string, action EzAction) {
	routeHub.Preparer[name] = &action
}
func AddFinisher(name string, action EzAction) {
	routeHub.Finisher[name] = &action
}

func AddBeforeHandler(name string, action EzAction) {
	routeHub.BeforeHandler[name] = &action
}
func AddAfterHandler(name string, action EzAction) {
	routeHub.AfterHandler[name] = &action
}

func GetRouteHub() *RouteHub {
	return routeHub
}
